﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

using BadHabit.Factory.Data;

namespace BadHabit.Factory.Controls {

    /// <summary>
    /// Draws the project burndown.
    /// 
    /// THIS IS A UGGLY, UGGLY CODE.
    /// MY FIRST WPF CONTROL, but still not a execuse for being so lazy.
    /// 
    /// One day i will refactor it...by now..it just works!
    /// 
    /// </summary>
    public partial class BurnDownGraph : UserControl {

        private bool rendering = false;

        // this is a key for the ShowOnlySprint property
        internal static readonly DependencyProperty ShowOnlySprintProperty =
            DependencyProperty.Register("ShowOnlySprint", typeof(short), typeof(BurnDownGraph), new PropertyMetadata(new PropertyChangedCallback(OnShowOnlySprintChanged)));


        // this is a key for the ShowOnlySprint property
        internal static readonly DependencyProperty WalkingPctProperty =
            DependencyProperty.Register("WalkingPct", typeof(double), typeof(BurnDownGraph), null);

        // this is a key for the ShowOnlySprint property
        internal static readonly DependencyProperty LateHrsProperty =
            DependencyProperty.Register("LateHrs", typeof(double), typeof(BurnDownGraph), null);


        // this is a key for the ShowOnlySprint property
        internal static readonly DependencyProperty IsLateProperty =
            DependencyProperty.Register("IsLate", typeof(bool), typeof(BurnDownGraph), null);



        private static void OnShowOnlySprintChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {            
            BurnDownGraph graph = d as BurnDownGraph;

            if (graph.Project == null)
                return;

            graph.DefineStartAndEndDate();
            graph.DefineMaxHours();
            
            graph.RenderGraph();
        }

        public bool AllowNavigation { get; set; }

        /// <summary>
        /// Gets and Sets the number of the sprint to show.
        /// Zero is all sprints.
        /// This is a dependency property.
        /// </summary>
        public short ShowOnlySprint {
            get { return (short)GetValue(ShowOnlySprintProperty); }
            set { SetValue(ShowOnlySprintProperty, value); }
        }

        public double WalkingPct {
            get { return (double)GetValue(WalkingPctProperty); }
            private set { SetValue(WalkingPctProperty, value); }
        }

        public double LateHrs {
            get { return (double)GetValue(LateHrsProperty); }
            private set { SetValue(LateHrsProperty, value); }
        }

        public bool IsLate {
            get { return (bool)GetValue(IsLateProperty); }
            private set { SetValue(IsLateProperty, value); }
        }

        /// <summary>
        /// Default constructor.
        /// </summary>
        public BurnDownGraph() {
            InitializeComponent();

            // sets the default colors            
            
            ToDoBackground = Resources["ToDoBrush"] as Brush;
            ToDoForeground = Resources["ToDoBorderBrush"] as Brush;
            WorkingBackground = Resources["WorkingBrush"] as Brush;
            WorkingForeground = Resources["WorkingBorderBrush"] as Brush;
            DoneBackground = Resources["DoneBrush"] as Brush;
            DoneForeground = Resources["DoneBorderBrush"] as Brush;

            CurrentDayBrush = Resources["CurrentDayBrush"] as Brush;

            PlannLineBrush = Resources["PlanLineBrush"] as Brush;

            //ToDoBackground = new SolidColorBrush(Colors.Red);
            //ToDoForeground = new SolidColorBrush(Colors.Red);
            //WorkingBackground = new SolidColorBrush(Colors.Yellow);
            //WorkingForeground = new SolidColorBrush(Colors.Yellow);
            //DoneBackground = new SolidColorBrush(Colors.Green);
            //DoneForeground = new SolidColorBrush(Colors.Green);

            //CurrentDayBrush = new SolidColorBrush(Colors.Green);

            //PlannLineBrush = new SolidColorBrush(Colors.Red);

            DataContextChanged += new DependencyPropertyChangedEventHandler(BurnDownGraph_DataContextChanged);


        }

        /// <summary>
        /// Gets the Graph Start Date.
        /// </summary>
        public DateTime StartDate { get; private set; }

        /// <summary>
        /// Gets the Graph End Date.
        /// </summary>
        public DateTime EndDate { get; private set; }

        /// <summary>
        /// Gets the DataContext as a Project.
        /// </summary>
        private ProjectDataSet Project {
            get {
                return DataContext as ProjectDataSet;
            }
        }


        /// <summary>
        /// Redraws the graph whenever the data context change.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void BurnDownGraph_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e) {

            if (Project == null)
                return;

            if (Project.Sprint.Rows.Count == 0)
                return;

            ShowOnlySprint = 0;

            ErrorMessagetextBlock.Visibility = Visibility.Hidden;
            try {

                DefineStartAndEndDate();

                DefineMaxHours();

                

                
            } catch (Exception ex) {
                ErrorMessagetextBlock.Text = ex.Message;
                ErrorMessagetextBlock.Visibility = Visibility.Visible;
                return;
            }

            
            RenderGraph();          
          
            
            

        }

        /// <summary>
        /// Calcls the max hours.
        /// </summary>
        private void DefineMaxHours() {

            Dictionary<int, double> baselineHours = new Dictionary<int, double>();

            foreach (ProjectDataSet.BacklogItemHoursRow hours in Project.BacklogItemHours) {

                if (ShowOnlySprint == 0 || ShowOnlySprint.Equals(hours.SprintRow.SprintNumber)) {
                    if (baselineHours.ContainsKey(hours.BaseLine))
                        baselineHours[hours.BaseLine] = baselineHours[hours.BaseLine] + hours.Quantity;
                    else
                        baselineHours.Add(hours.BaseLine, hours.Quantity);
                }
                
            }

            // get rid off the proposal hours (baseline 0)
            if (baselineHours.Count > 1)
                baselineHours.Remove(0);

            if (baselineHours.Count == 0) {
                MaxHours = 0;
                return;
            }

            // if all sprints should be render, then use the max of the baselines
            if (ShowOnlySprint == 0) {
                MaxHours = baselineHours.Values.Max();
                InitialPlannedHours = CalcQuantityForDay(StartDate.AddDays(-1).Date, 1);
            } else {
                InitialPlannedHours = CalcQuantityForDay(StartDate.AddDays(-1).Date, ShowOnlySprint);
                if (baselineHours.ContainsKey(ShowOnlySprint))
                    MaxHours = baselineHours[ShowOnlySprint];
                else
                    MaxHours = baselineHours[baselineHours.Keys.Max()];
            }
        }

        private Polygon Area { get; set; }

        /// <summary>
        /// Renders the graph.
        /// </summary>
        public void RenderGraph() {

            GraphPanel.Children.Clear();
            DayScalePanel.Children.Clear();

            if (Project == null) {
                ShowErrorMessage(Properties.Resources.Can_not_create_graph);
                return;
            }

            if (Project.Project.Count == 0) {
                ShowErrorMessage(Properties.Resources.Can_not_create_graph);
                return;
            }


            if (Project.Sprint.Rows.Count == 0) {
                ShowErrorMessage(Properties.Resources.Your_project_does_not_have_sprints_yet);
                return;
            }
            
            if (Project.Project[0].HourBankMode != 0) {
                ShowErrorMessage(Properties.Resources.Maintance_projects_does_not_have_bundown_graphs);
                return;
            }
                        
            if (rendering)
                return;

            rendering = true;
            this.Cursor = Cursors.Wait;
            ErrorMessagetextBlock.Visibility = Visibility.Hidden;

            try {

                

                BarWidth = GraphPanel.ActualWidth / DayCount;

                
                dayWalked = 0;
                

                DefineHeightFactor();
                
                Area = new Polygon();
                Area.Points.Add(new Point(0, GraphPanel.ActualHeight));                
                //Area.Points.Add(new Point(0, 0));


                // creates one grid for each sprint
                DateTime sprintStart = StartDate;
                double left = 0;
                foreach (ProjectDataSet.SprintRow sprint in Project.Sprint.Select("", "ExpirationDate")) {

                    if (ShowOnlySprint == 0 || ShowOnlySprint.Equals(sprint.SprintNumber)) {
                        
                        StackPanel sprintGrid = CreateSprintGrid(sprintStart, sprint);
                        sprintGrid.VerticalAlignment = VerticalAlignment.Bottom;
                        Grid dayScaleGrid = CreateDayScaleGrid(sprintStart, sprint);

                        sprintGrid.SetValue(Canvas.BottomProperty, 0.0);
                        sprintGrid.SetValue(Canvas.LeftProperty, left);

                        GraphPanel.Children.Add(sprintGrid);
                        DayScalePanel.Children.Add(dayScaleGrid);


                        sprintStart = sprint.ExpirationDate.AddDays(1);                        
                        left = left + sprintGrid.Width;
                    }
                }

                if (ShowOnlySprint == 0) {
                    Point lastPoint = Area.Points.Last<Point>();
                    Area.Points.Add(new Point(lastPoint.X, lastPoint.Y));
                    Area.Points.Add(new Point(lastPoint.X, GraphPanel.ActualHeight));
                    GraphPanel.Children.Add(Area);
                }

                Area.Points.Freeze();


                

                DrawHourGridLines();


                DrawPlannedLine();

                DrawYAxisScale();

                double dayHr = InitialPlannedHours / DayCount;
                double shouldBeWalked = dayWalked * dayHr;

                double walkPct = 0;
                if(InitialPlannedHours>0)
                    walkPct = ((InitialPlannedHours - lastToDoCount) / InitialPlannedHours) * 100;
                // IT MAY HAPPEN THAT THE PROJECT INITIAL PLANNED HOURS ARE SHORTER THAN
                // THE ACTTUCAL TODO HOURS, SOU YOU WALK BACKWARDS
                //if(walkPct<0)
                //    walkPct = 0;
                WalkingPct = walkPct;

                double latehrs = shouldBeWalked - (InitialPlannedHours - lastToDoCount);
                if (latehrs <= 0) {
                    LateHrs = (shouldBeWalked - (InitialPlannedHours - lastToDoCount)) * -1;
                    IsLate = false;
                } else {
                    LateHrs = (shouldBeWalked - (InitialPlannedHours - lastToDoCount));
                    IsLate = true;
                }
                

            } catch (Exception ex) {
                ShowErrorMessage(ex.Message);                
            }

            rendering = false;
            this.Cursor = Cursors.Arrow;

        }

        private void ShowErrorMessage(string message) {
            ErrorMessagetextBlock.Text = message;
            ErrorMessagetextBlock.Visibility = Visibility.Visible;
        }

        /// <summary>
        /// Draws YAxis scale.
        /// </summary>
        private void DrawYAxisScale() {

            YAxisScalePanel.Children.Clear();

            double offSet = GraphPanel.ActualHeight / 10;
            double hourOffSet = MaxHours / 10;
            
            for (int i = 1; i < 10; i++) {
                TextBlock scale = new TextBlock();
                scale.Text = Math.Round(MaxHours - (i*hourOffSet), 0).ToString("000");
                scale.TextAlignment = TextAlignment.Right;
                Viewbox vb = new Viewbox();
                vb.HorizontalAlignment = HorizontalAlignment.Left;
                vb.Width = YAxisScalePanel.ActualWidth - 3;
                vb.Margin = new Thickness(0, -offSet/2, 0, 0);
                vb.Child = scale;
                vb.SetValue(Canvas.TopProperty, offSet * i);

                YAxisScalePanel.Children.Add(vb);                
            }
        }

        /// <summary>
        /// Draws the hour grid lines.
        /// </summary>
        private void DrawHourGridLines() {
            double offSet = GraphPanel.ActualHeight / 10;
            for (int i = 1; i < 10; i++) {
                Line l = new Line();
                l.X1 = -3;
                l.Y1 = GraphPanel.ActualHeight - (i * offSet);
                l.X2 = GraphPanel.ActualWidth;
                l.Y2 = l.Y1;
                l.Stroke = Brushes.Gray;
                DoubleCollection d = new DoubleCollection();
                d.Add(1);
                d.Add(2);
                l.StrokeDashArray = d;
                l.StrokeThickness = 1;
                
                GraphPanel.Children.Add(l);
            }
        }


        private double InitialPlannedHours { get; set; }
            

        /// <summary>
        /// Draw a the planned limit line.
        /// </summary>
        private void DrawPlannedLine() {
            Line l = new Line();
            l.X1 = 0;
            l.Y1 = GraphPanel.ActualHeight - (InitialPlannedHours*BarHeightFactor);
            l.X2 = GraphPanel.ActualWidth;
            l.Y2 = GraphPanel.ActualHeight;
            l.Stroke = PlannLineBrush;
            l.StrokeDashCap = PenLineCap.Round;
            l.StrokeThickness = 2;
            l.StrokeStartLineCap = PenLineCap.Round;
            l.StrokeEndLineCap = PenLineCap.Round;
            DoubleCollection d = new DoubleCollection();
            d.Add(3);
            d.Add(2);
            l.StrokeDashArray = d;
            l.ToolTip = "Planned Limit";
            l.Cursor = Cursors.Help;
            GraphPanel.Children.Add(l);
        }

        private int dayWalked = 0;
        

        /// <summary>
        /// Creates a Grid for a Sprint.
        /// </summary>
        /// <param name="sprintStart">The previous sprint end</param>
        /// <param name="sprint">The sprint row</param>
        /// <returns>The grid</returns>
        private StackPanel CreateSprintGrid(DateTime sprintStart, ProjectDataSet.SprintRow sprint) {
            
            StackPanel sprintGrid = new StackPanel();
            sprintGrid.Orientation = Orientation.Horizontal;
            
            // create de days columns            
            int sprintDayCount = sprint.ExpirationDate.Subtract(sprintStart).Days;
            int sprintWorkDayCount = sprint.WorkDayCount;


            sprintGrid.Width = sprintWorkDayCount * BarWidth;
  
            // adds the day bar and labels                        
            DateTime day = sprintStart;
            int column = 0;
            
            // while i did not hit the end date
            while(day.Date <= sprint.ExpirationDate) {
                         
                // jumps the day offs
                if (Data.ProjectDataSet.IsWorkDay(day.Date)) {

                    StackPanel barPanel = new StackPanel();
                    barPanel.Orientation = Orientation.Vertical;
                    barPanel.VerticalAlignment = VerticalAlignment.Bottom;
                    barPanel.Width = BarWidth;
                    sprintGrid.Children.Add(barPanel);

                    // if is a past date, show in the graph
                    if (day.Date <= DateTime.Today.Date) {

                        dayWalked++;

                        // adds the stack bar panel
                        // if is a bar graph
                        if (ShowOnlySprint > 0) {
                            lastToDoCount = 0;
                            // adds the done bars
                            AddBars(barPanel, BadHabit.Factory.BacklogItemStatus.DONE, day, sprint.SprintUId, sprint.SprintNumber);
                            // adds the working bars
                            AddBars(barPanel, BadHabit.Factory.BacklogItemStatus.WORKING, day, sprint.SprintUId, sprint.SprintNumber);
                            // adds the toDo bar                                
                            AddBars(barPanel, BadHabit.Factory.BacklogItemStatus.REQUIRED, day, sprint.SprintUId, sprint.SprintNumber);
                        } else {
                            lastToDoCount = 0;
                            AddArea(day, sprint.SprintNumber);
                        }
                    } else {
                        AddFurtherArea(barPanel, day, sprint.SprintNumber);
                    }
                                                                        
                    column++;
                }
                
                day = day.AddDays(1);
            }

            

            return sprintGrid;
        }

        private void AddFurtherArea(StackPanel barPanel, DateTime day, short sprintNumber) {
            // if is s further date, show gray
            Rectangle bar = new Rectangle();
            bar.Fill = Brushes.LightGray;
            bar.StrokeThickness = 0;
            lastToDoCount = CalcQuantityForDay(day, sprintNumber);            
            bar.Height = lastToDoCount * BarHeightFactor;
            bar.Width = BarWidth;
            barPanel.Children.Add(bar);

        }

        /// <summary>
        /// Creates a Grid for a Sprint.
        /// </summary>
        /// <param name="sprintStart">The previous sprint end</param>
        /// <param name="sprint">The sprint row</param>
        /// <returns>The grid</returns>
        private Grid CreateDayScaleGrid(DateTime sprintStart, ProjectDataSet.SprintRow sprint) {
            Grid sprintGrid = new Grid();

            sprintGrid.VerticalAlignment = VerticalAlignment.Stretch;
            sprintGrid.HorizontalAlignment = HorizontalAlignment.Stretch;

            // create de rows            
            sprintGrid.RowDefinitions.Add(new RowDefinition());
            sprintGrid.RowDefinitions.Add(new RowDefinition());
            if (BarWidth <= 10)
                sprintGrid.RowDefinitions[0].Height = new GridLength(0, GridUnitType.Pixel);
            else {
                //sprintGrid.RowDefinitions[0].Height = new GridLength(DayTextHeight, GridUnitType.Pixel);
                sprintGrid.RowDefinitions[0].Height = new GridLength(0.5, GridUnitType.Star);
            }
            
            //sprintGrid.RowDefinitions[1].Height = new GridLength(DayTextHeight, GridUnitType.Pixel);
            sprintGrid.RowDefinitions[1].Height = new GridLength(0.5, GridUnitType.Star);
            
            // create de days columns            
            int sprintDayCount = sprint.ExpirationDate.Subtract(sprintStart).Days;
            int sprintWorkDayCount = sprint.WorkDayCount;

            if (sprintDayCount <= 0 || sprintWorkDayCount <= 0)
                throw new Exception("Invalid Sprint size.");

            for (int i = 0; i < sprintWorkDayCount; i++) {
                sprintGrid.ColumnDefinitions.Add(new ColumnDefinition());
                sprintGrid.ColumnDefinitions[i].Width = new GridLength(BarWidth, GridUnitType.Pixel);
            }

            
            // adds the day bar and labels            
            DateTime day = sprintStart;
            int column = 0;
            while (day.Date <= sprint.ExpirationDate.Date) {

                if (Data.ProjectDataSet.IsWorkDay(day.Date)) {

                    // adds the day                   
                    if (BarWidth > 10) {
                        Viewbox vb = new Viewbox();

                        TextBlock dayText = new TextBlock();
                        dayText.Text = day.Day.ToString("00") + "\n" + day.DayOfWeek.ToString().Substring(0, 1);
                        dayText.TextAlignment = TextAlignment.Center;

                        if (DateTime.Today.Equals(day.Date))
                            dayText.Foreground = CurrentDayBrush;
                        else
                            dayText.Foreground = Brushes.Gray;

                        
                        
                        vb.Child = dayText;

                        vb.SetValue(Grid.RowProperty, 0);
                        vb.SetValue(Grid.ColumnProperty, column);
                        sprintGrid.Children.Add(vb);

                        

                    }                

                    column++;
                }

                day = day.AddDays(1);

            }
            
            
            // adds the title                       
            TextBlock title = new TextBlock();
            title.Text = sprint.Title;            
            title.HorizontalAlignment = HorizontalAlignment.Stretch;
            title.VerticalAlignment = VerticalAlignment.Stretch;
            title.TextAlignment = TextAlignment.Center;                        
            title.IsHitTestVisible = false;

            Viewbox vb2 = new Viewbox();
            vb2.Child = title;
            vb2.MaxHeight = DayTextHeight * 0.8;
            vb2.VerticalAlignment = VerticalAlignment.Stretch;

                        
            Border bb = new Border();
            if (AllowNavigation) {
                bb.Style = (Style)Application.Current.Resources["HighLightBorder"];
                bb.MouseLeftButtonUp += new MouseButtonEventHandler(bb_MouseLeftButtonUp);
            }
            bb.Margin = new Thickness(0, 1, 0, 0);
            bb.Padding = new Thickness(0, 0, 0, 0);
            bb.Child = vb2;
            bb.HorizontalAlignment = HorizontalAlignment.Stretch;
            bb.Tag = sprint;
            



            Border b = new Border();
            b.Child = bb;
            b.BorderThickness = new Thickness(0, 5, 0, 0);            
            if(sprint.SprintNumber % 2==0)
                b.BorderBrush = Brushes.LightGray;
            else
                b.BorderBrush = Brushes.Gray;
            b.HorizontalAlignment = HorizontalAlignment.Stretch;
            b.VerticalAlignment = VerticalAlignment.Stretch;
            b.SetValue(Grid.RowProperty, 1);
            b.SetValue(Grid.ColumnSpanProperty, sprintWorkDayCount);

            ToolTip tooltip = new ToolTip();
            TextBlock tooltipText = new TextBlock();
            tooltipText.Text = "From " + sprintStart.ToShortDateString() + " to " + sprint.ExpirationDate.ToShortDateString();
            tooltip.Content = tooltipText;

            b.ToolTip = tooltip;

            
            
            sprintGrid.Children.Add(b);

            
            
            return sprintGrid;
        }

        void bb_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) {
            Border b = e.OriginalSource as Border;
            Data.ProjectDataSet.SprintRow sprint = b.Tag as Data.ProjectDataSet.SprintRow;
            ShowOnlySprint = sprint.SprintNumber;
            RenderGraph();
        }


        /// <summary>
        /// Adds a bar for each backlogitem at the given status at a given day of a sprint.
        /// </summary>
        /// <param name="panel">The panel to add the bars</param>
        /// <param name="status">The Status</param>
        /// <param name="day">The day</param>
        /// <param name="sprintUId">The sprint</param>        
        private void AddBars(StackPanel panel, BadHabit.Factory.BacklogItemStatus status, DateTime day, string sprintUId, short sprintNumber) {
            if (Project == null)
                return;

            if (!ShowDoneBars && BadHabit.Factory.BacklogItemStatus.DONE.Equals(status))
                return;

            Brush barBackground = Brushes.Gray;
            Brush barForeground = Brushes.DarkGray;

            // first selects the itens with that status at that day
            string filter = "1 = 1 AND ";
            string sort = "BusinessValue DESC, BacklogItemNumber";
            switch (status) {
                case BadHabit.Factory.BacklogItemStatus.DONE:
                    barForeground = DoneForeground;
                    barBackground = DoneBackground;
                    filter = "(FinishDate <= '" + day.AddDays(1).Date + "')";                    
                    sort = "FinishDate";
                    break;
                case BadHabit.Factory.BacklogItemStatus.REQUIRED:
                    barForeground = ToDoForeground;
                    barBackground = ToDoBackground;
                    //filter = "(StartDate > '" + day + "' or StartDate is null)";
                    filter = "(StartDate > '" + day + "' or StartDate is null) and (FinishDate >'" + day.AddDays(1).Date + "' or FinishDate is null)";
                    sort = "BusinessValue DESC, BacklogItemNumber";
                    break;                
                case BadHabit.Factory.BacklogItemStatus.WORKING:
                    barForeground = WorkingForeground;
                    barBackground = WorkingBackground;
                    filter = "(StartDate <= '" + day + "' and (FinishDate >'" + day.AddDays(1).Date + "' or FinishDate is null))";
                    sort = "StartDate";
                    break;
                
            }
                        
            ProjectDataSet.BacklogItemRow[] items = (ProjectDataSet.BacklogItemRow[])Project.BacklogItem.Select(filter, sort);

            // should use only the hours planned at that sprint
            // if sprint had not been planned yet, use the project baseline
            int baseLine = Project.Project[0].Baseline;
            if (sprintNumber < baseLine)
                baseLine = sprintNumber;
                        
            // now adds a bar for each item            
            double count = 0;
            for (int i = 0; i < items.Length; i++) {                
                foreach (ProjectDataSet.BacklogItemHoursRow h in items[i].GetBacklogItemHoursRows()) {

                    // only count hours of this baseline
                    // and if ShowOnlySprint is setted, only of this sprint
                    if (baseLine.Equals(h.BaseLine) && (ShowOnlySprint==0 || ShowOnlySprint.Equals(h.SprintRow.SprintNumber)))
                        count = count + h.Quantity;                     
                }
                
                Rectangle bar = new Rectangle();                                        
                bar.Fill = barBackground;
                bar.Stroke = barForeground;
                bar.Height = count * BarHeightFactor;
                bar.Width = BarWidth;
                
                panel.Children.Add(bar);
            
            
                // creates the tooltip, if not viewing the whole project            
                ToolTip tooltip = new ToolTip();
                TextBlock tooltipText = new TextBlock();
                tooltipText.Text = items[i].Title + "\nTo Do Hours: " + count + " hrs";
                tooltip.Content = tooltipText;

                bar.ToolTip = tooltip;
                bar.Cursor = Cursors.Help;

                if (status == BadHabit.Factory.BacklogItemStatus.REQUIRED || status == BadHabit.Factory.BacklogItemStatus.WORKING)
                    lastToDoCount = lastToDoCount + count;
                
                count = 0;


            }
            
        }

        private double lastToDoCount = 0;


        private double CalcQuantityForDay(DateTime day,short sprintNumber) {
            if (Project == null)
                return 0;

            string filter = "1 = 1 AND ";
            string sort = "BusinessValue DESC, BacklogItemNumber";
            filter = "(FinishDate >'" + day.AddDays(1).Date + "' or FinishDate is null)";

            ProjectDataSet.BacklogItemRow[] items = (ProjectDataSet.BacklogItemRow[])Project.BacklogItem.Select(filter, sort);

            // should use only the hours planned at that sprint
            // if sprint had not been planned yet, use the project baseline
            int baseLine = Project.Project[0].Baseline;
            if (sprintNumber < baseLine)
                baseLine = sprintNumber;

            // now adds a bar for each item            
            double count = 0;
            for (int i = 0; i < items.Length; i++) {
                foreach (ProjectDataSet.BacklogItemHoursRow h in items[i].GetBacklogItemHoursRows()) {
                    // only count hours of this baseline
                    // and if ShowOnlySprint is setted, only of this sprint
                    if (baseLine.Equals(h.BaseLine) && (ShowOnlySprint == 0 || ShowOnlySprint.Equals(h.SprintRow.SprintNumber)))
                        count = count + h.Quantity;
                }

            }

            return count;

        }

        private void AddArea(DateTime day, short sprintNumber) {
            if (Project == null)
                return;


            double count = CalcQuantityForDay(day, sprintNumber);


            Area.Fill = (Brush) Resources["AreaBrush"];
            Area.Stroke = (Brush)Resources["AreaBrush"];
            Area.StrokeThickness = 2;

            if (day.Date <= DateTime.Today) {
                Point lastPoint = Area.Points.Last<Point>();

                // if is the first day, add a firts point at the area
                if (day.Date.Equals(StartDate.Date))
                    Area.Points.Add(new Point(lastPoint.X, GraphPanel.ActualHeight - (InitialPlannedHours * BarHeightFactor)));

                Area.Points.Add(new Point(lastPoint.X + BarWidth, GraphPanel.ActualHeight - (count * BarHeightFactor)));
            }

            lastToDoCount = count;

                        
        }


        /// <summary>
        /// Defines the graph day period.
        /// </summary>
        private void DefineStartAndEndDate() {
            if(Project==null)
                return;

            if (Project.Sprint.Rows.Count == 0)
                return;


            DateTime start = Project.Project[0].FirstSprint.StartDate;
            DateTime end = Project.Project[0].LastSprint.ExpirationDate;
            

            // if is to show only one sprint
            if (ShowOnlySprint > 0) {
                
                // the start should be the sprint before expiration
                start = SprintToShow.StartDate;

                // the end is easy, its the expiration day
                end = SprintToShow.ExpirationDate;
            }
            

            StartDate = start;
            EndDate = end;


            DayCount = Data.ProjectDataSet.CalcWorkDayCount(StartDate, EndDate);

            

        }

        /// <summary>
        /// Gets the sprint to be show at the graph in case of ShowOnlySprint is greater than zero.
        /// </summary>
        private ProjectDataSet.SprintRow SprintToShow {
            get {
                return Project.Project[0].FindSprintByNumber(ShowOnlySprint);                                   
            }
        }

        

        

        /// <summary>
        /// Define the height factor.
        /// </summary>
        private void DefineHeightFactor() {
            if (Project == null)
                return;

            DayTextHeight = GraphPanel.ActualHeight * 0.06;
            
            if (MaxHours > 0)
                BarHeightFactor = GraphPanel.ActualHeight / MaxHours;
            else
                BarHeightFactor = GraphPanel.ActualHeight;
        }

     
        /// <summary>
        /// Gets and Sets the number of days showing at the graph.
        /// </summary>
        private int DayCount { get; set; }

        /// <summary>
        /// Gets and Sets the bar with.
        /// </summary>
        private double BarWidth { get; set; }

        /// <summary>
        /// Gets and Sets the bar height factor.
        /// </summary>
        private double BarHeightFactor { get; set; }

        /// <summary>
        /// Gets and sets the the to do bar background.
        /// </summary>
        public Brush ToDoBackground { get; set; }

        /// <summary>
        /// Gets and sets the the working bar background.
        /// </summary>
        public Brush WorkingBackground { get; set; }

        /// <summary>
        /// Gets and sets the the done bar background.
        /// </summary>
        public Brush DoneBackground { get; set; }

        /// <summary>
        /// Gets and sets the the to do bar foreground.
        /// </summary>
        public Brush ToDoForeground { get; set; }

        /// <summary>
        /// Gets and sets the the working bar foreground.
        /// </summary>
        public Brush WorkingForeground { get; set; }

        /// <summary>
        /// Gets and sets the the done bar foreground.
        /// </summary>
        public Brush DoneForeground { get; set; }

        /// <summary>
        /// Gets and sets the the done bar foreground.
        /// </summary>
        public Brush PlannLineBrush { get; set; }

        /// <summary>
        /// Gets and sets the day text height.
        /// </summary>
        private double DayTextHeight { get; set; }


        /// <summary>
        /// Gets and sets when the done bars should be showed.
        /// </summary>
        public bool ShowDoneBars { get; set; }


        private double MaxHours { get; set; }

        public Brush CurrentDayBrush { get; set; }


        
        private void UserControl_SizeChanged(object sender, SizeChangedEventArgs e) {
            RenderGraph();        
        }


     
    }
}
